/* COMBINE is a small module giving combination
   for a given size   (maths C n;p )

   the process start with p highest positions set to 1
   and finish with p lowest positions sets to 
   the lowest digit goes down till the lowest position, then the next ...

*/
#define _CRT_SECURE_NO_DEPRECATE

#include <fstream>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#include "skfrtype.h"

#include "utilities.h"
#include "sk_tables.cpp"
extern FOUTPUT output1;
void FSR::E(const char * c)   {if(pron) output1<<c;  prl+= strlen(c);}
void FSR::E(const char c)   {if(pron) output1 <<c;  prl++;}
void FSR::E(const int x)    {if(pron) output1<<x;  prl+=5;}

void FSR::Enl()       {if(pron) output1<<endl;}
void FSR::Enl(const char * c)   {if(pron) output1<<c<<endl;  prl+= strlen(c)+1;}
void FSR::Enl(const char c)   {if(pron) output1<<c<<endl;  prl+=2;}
void FSR::Enl(const int x)    {if(pron) output1<<x<<endl;  prl+=2;}
void FSR::Ehex(const int x)    {if(pron) output1<<"0x"<<hex<<x<<dec;  prl+=7;}

// catching time as seconds+millis  (seconds since year 1970)
long GetTimeMillis() {
	struct _timeb tbuf;
	_ftime64_s(&tbuf); 
	return ((long)(1000 * tbuf.time) + tbuf.millitm);
}

//=============================
//====================== to be in line with borland string 
char * stpcpy(char * d, const char * o)
	{strcpy(d,o); return (d+strlen(d));}

char * Blancs(int n,int pastrait)
 {static char wt[]="___________________ ",
              wn[]="                    ";
              if(pastrait)return &wn[20-n]; else return &wt[20-n];}

void BUILDSTRING::Astr(const char * x){
	if(strlen(x)<=(unsigned int)(lim-p))
		p=stpcpy(p,x);
}
//==========================================BUILDSTRING
void BUILDSTRING::Close(){
	if(p<zs || p>lim)	{	zs[0]=0; p=zs;}
	else	(*p)=0;
}

void BUILDSTRING::Aunit(int x){
	char *t="RCB";
	(*p++)=t[x/9];
	Achint(x%9);
}

void BUILDSTRING::AScand(SCAND x){
	if(x & (1<<11)) (*p++)='~';
	char dg= ((x>>7) & 15) +'0';
	(*p++)=dg+1;
	Astr(cellsFixedData[x &0x7f].pt);
}

void BUILDSTRING::Store(SCAND & w){
		USHORT ws=w>>11,wd=(w>>7) & 15, wc=w & 0x7f;
		Astr(ws? " ~":" ");
		Achint(wd);
		Astr(cellsFixedData[wc].pt);
}

void BUILDSTRING::StoreChain(SCAND * t, int n){
	for(int i=0;i<n;i++)	Store(t[i]);
	Aendl();
}



void BUILDSTRING::StoreX(SCAND & w,int dig){
		USHORT ws=w>>7, wc=w & 0x7f;
		Astr(ws? " ~":" ");
		Achint(dig);
		Astr(cellsFixedData[wc].pt);
}

void BUILDSTRING::StoreXChain(SCAND * t, int n,int dig){
	Astr(" xchain ");
	for(int i=0;i<n;i++)	StoreX(t[i],dig);
	Aendl();
}

int CELL_FIX::GetTableRegions(USHORT * tt,CELL_FIX & cell2){
	int n=0;
	if(el==cell2.el) tt[n++]=el;
	if(pl==cell2.pl) tt[n++]=plu;
	if(eb==cell2.eb) tt[n++]=ebu;
	return n;
}



/* First is the initial call for the task
   n;p is the classical math sense

   n total number of objects
   p number of object to select (maxi is p=10)

   d in the entry table
   f is the result table  both size n
*/
 
 void COMBINE::First(int ne,int pe,USHORT * d, USHORT * f )    {
	 entry=d; 
	 output=f;   
	 p=pe;  
	 if(p>10)
		 p=10;  // safety measure, not auhorised
	 lim=p-1; 
	 n=ne;
     for(UINT i=0,v=n-p;i<p;i++,v++) 
		 inds[i]=v;
     Sort();
 }
 /* move step by step from p upper index to p lower index
    return 1 if ok;  0 if over
 */
 int COMBINE::Next()  {
	 if(inds[lim]==lim) 
		 return 0; // finished
     int i=0; 
	 while(inds[i]==i) 
		 i++; // lock for first that can go down
	 inds[i]--; // back one for that one
	 UINT v=inds[i];  // an restart below with no hole
	 while(i--) inds[i]=--v;
	 Sort();    
	 return 1;}

/* send back the entry table with first p as the selection
   others after the selection
   inds is the list of "p" index selected
*/

 void COMBINE::Sort()   { 
	 UINT is=0,js=n-p,jns=0;
	 for(UINT i=0;i<n;i++) {
		 if(i-inds[is] ){
		   output[jns++]=entry[i]; 
		   continue;
		}
	    output[js++]=entry[i];
		if(is<p-1)   // don't pass the limit of the table inds
			is++;   
	 }   

 }  

                //=============== PUZ0

 void PUZ0::Create(char * ze){
	 for(int i=0;i<81;i++){
		 char c=ze[i];
		 if(c>'0' && c<='9')
			 puz[i]=c-'0';
		 else
			puz[i]=0;
	 }
 }

 void PUZ0::Empty(){
	 for(int i=0;i<81;i++)
		 puz[i]=0;	
 }

char * PUZ0::String(char * zout){
	 for(int i=0;i<81;i++)
		 if(puz[i])
			zout[i]=puz[i]+'0';
		 else
			 zout[i]='.';
	 zout[81]=0;
	 return zout;
}

void PUZ0::TableClues(USHORT * tt,USHORT & itt){
	itt=0;
	for(int i=0;i<81;i++)
		if(puz[i])  // assumed 0 based
			tt[itt++]=i;		
}

                //=============== PUZC

 void PUZC::Create(char * ze){
	 for(int i=0;i<81;i++){
		 char c=ze[i];
		 if(c>'0' && c<='9')
			 puz[i]=c;
		 else
			 puz[i]='.';
	 }
	 puz[81]=0;
 }

 void PUZC::CreateFromZeroBased(char * ze){
	 for(int i=0;i<81;i++){
		 char c=ze[i];
		 if(c)
			 puz[i]=c+'0';
		 else
			 puz[i]='.';
	 }
	 puz[81]=0;
 }

void PUZC::Force_0(){
	 for(int i=0;i<81;i++)	 if( puz[i]='.')
		 	 puz[i]='0';
 }

void PUZC::Morph(char * ze, USHORT * rows,USHORT * cols){
	puz[81]=0;
	for(int i=0;i<9;i++) for(int j=0;j<9;j++)
		puz[9*i+j]=ze[9*rows[i]+cols[j]];
}

void PUZC::DoMainDiagSymmetry(){
	char zx[82];
	strcpy (zx,puz);
	for(int i=0;i<9;i++)		for(int j=0;j<9;j++)
			puz[9*i+j]=zx[9*j+i];	
}

int PUZC::IsNotPatMainDiagSymmetry(){ // only on pattern
	char zx[82];
	strcpy (zx,puz);
	for(int i=0;i<9;i++)		for(int j=i+1;j<9;j++)
		if(	puz[9*i+j]-zx[9*j+i]) return 1;
	return 0;
}

void PUZC::TableClues(USHORT * tt,USHORT & itt){
	itt=0;
	for(int i=0;i<81;i++)
		if(puz[i]-'.')   
			tt[itt++]=i;		
}

void PUZC_SYM::GetPuz(char * ze){
	strcpy (puz,ze); DoGiven();
}

void PUZC_SYM::Rotate(char * ze){
	puz[81]=0;
	for(int i=0;i<9;i++){
		int row=8-i,col=9+i;
		for(int j=0;j<9;j++){
			puz[cellsInGroup[row][j]]=ze[cellsInGroup[col][j]];
		}
	}
	DoGiven();
}

void PUZC_SYM::DoGiven(){
	given.SetAll_0();
	for(int i=0;i<81;i++) if(puz[i]-'.') given.Set(i);
}

int PUZC_SYM::CheckBM(USHORT * data,int n){
	for(int i=0,p=0;i<n;i++,p+=2){
		int a=(given.On(data[p]))?1:0,b=(given.On(data[p+1]))?1:0;
		if(a^b) return 0;
	}
	return 1;
}

int PUZC_SYM::CheckBM_R90(){
	for(int i=0,p=0;i<20;i++,p+=2){
		USHORT * data=sr90_20[i];
		int a=(given.On(data[0]))?1:0,b=(given.On(data[1]))?1:0,c=(given.On(data[2]))?1:0,d=(given.On(data[3]))?1:0;
		if((a^b)|| (a^c) || (a^d) )return 0;
	}
	return 1;
}

void PUZC_SYM::BandCount(){
	for(int i=0;i<6;i++){
		BF81 ww(cellsInBandBM[i]);
		nband[i]=(ww&given).Count();
	}

}
void PUZC_SYM::BoxCount(){
	for(int i=18;i<27;i++){
		BF81 ww(cellsInHouseBM[i]);
		nbox[i-18]=(ww&given).Count();
	}
}
void PUZC_SYM::RowCount(){
	for(int i=0;i<9;i++){
		BF81 ww(cellsInHouseBM[i]);
		nrow[i]=(ww&given).Count();
	}
}

int PUZC_SYM::NoMatchBand(USHORT * ib,USHORT * nb){
	for(int i=0;i<3;i++){
		USHORT indb=ib[i];
		if(nband[i]-nb[indb]) return 1;		
	}
	return 0;
}
void PUZC_SYM::MoveBands(char * ze,USHORT bhp,USHORT bvp){
	// create a morph of the puzzles and compute given in bands and boxes
	for(int i=0;i<3;i++)for(int j=0;j<3;j++){
		USHORT box= 3*perms_band[bhp][i]+perms_band[bvp][j]+18,
			   boxd=3*i+j+18;
		for(int k=0;k<9;k++){
			puz[cellsInGroup[boxd][k]]=ze[cellsInGroup[box][k]];
		}
	}
	DoGiven(); BandCount(); BoxCount();

}

void PUZC_SYM::MoveBSx(char * zd,char * ze,USHORT bx,USHORT dx){
	// create a morph 3
	strcpy (zd,ze); 
	for(int i=0;i<3;i++){
		USHORT r0=dx+i,rr=dx+perms_band[bx][i];
		if(r0==rr) continue;
		for(int k=0;k<9;k++){
			zd[cellsInGroup[r0][k]]=ze[cellsInGroup[rr][k]];
		}
	}
}

void PUZC_SYM::MoveBxSx(char * ze,USHORT bande,USHORT stack,USHORT drow,USHORT dcol){
	// create a morph in band 3 and stack 3
	char puzw0[82];
	MoveBSx(puzw0,ze,bande,drow);
	MoveBSx(puz,puzw0,stack,dcol);
	DoGiven();
}

void PUZC_SYM::ExchangeBoxes(char * ze,USHORT box1,USHORT box2){
	GetPuz(ze); // default if error or no morph
	if(box1==box2) 	return;
	if(box1<18 || box2<18) return; // not valid
	USHORT box1x=box1-18,box2x=box2-18,
		   bande1=box1x/3,bande2=box2x/3,
		   stack1=box1x%3,stack2=box2x%3,
		   trows[9],tcols[9];
	for(int i=0;i<9;i++) trows[i]=tcols[i]=i;
	if(bande1-bande2){
		for(int i=0;i<3;i++){ // exchange rows
			trows[3*bande1 + i]=3*bande2 + i;
			trows[3*bande2 + i]=3*bande1 + i;
		}
	}
	if(stack1-stack2){
		for(int i=0;i<3;i++){ // exchange rows
			tcols[3*stack1 + i]=3*stack2 + i;
			tcols[3*stack2 + i]=3*stack1 + i;
		}
	}
	Morph(ze,trows,tcols);
	DoGiven();
}


void PUZC_SYM::MoveB1B3(char * ze,USHORT bx ){
	// create a morph moving rows keeping central symmetry in bands 1 3
	strcpy (puz,ze); 
	for(int i=0;i<3;i++){
		USHORT r0=i,rr=perms_band[bx][i];
		if(r0==rr) continue;
		for(int k=0;k<9;k++){
			puz[cellsInGroup[r0][k]]=ze[cellsInGroup[rr][k]];
			puz[cellsInGroup[8-r0][k]]=ze[cellsInGroup[8-rr][k]];

		}
	}
}

int PUZC_SYM::IsCentralBoxes(){
	for(int i=0;i<5;i++) if(nbox[i]-nbox[8-i])
		return 0;
//	cout<<"central box "<< endl;
//	cout << puz<<endl;
	// try now box 5
	char zr[82];
	strcpy (zr ,puz);
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){
			MoveBxSx(zr,i,j,3,12);
			if(IsCentralBox5())
				return 1;
		}
	}
	return 0;
}

int PUZC_SYM::IsCentralBox5(){
	USHORT tchk[10]={30,50,31,49,32,48,39,41};
	if(!CheckBM(tchk,4))	return 0;
	// try all possibilities in band3 stack 3 to end 
//	cout<<"okbox5"<<endl;
//	cout << puz<<endl;
	char zr[82];
	strcpy (zr ,puz);
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){
			MoveBxSx(zr,i,j,6,15);
	//cout<<"test central ij="<<i<<j<<endl;
	//cout << puz<<endl;
			if(CheckBM(sc_40[0],40))
				return 1;
		}
	}
	return 0;
}

int PUZC_SYM::IsDiagBoxes(){
	if(nbox[1]-nbox[3])		return 0;
	if(nbox[2]-nbox[6])		return 0;
	if(nbox[5]-nbox[7])		return 0;
	// try now box 5
	char zr[82];
	strcpy (zr ,puz);
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){
			MoveBxSx(zr,i,j,3,12);
	//		cout <<"perm5i="<<i<<" perm5j="<<j<<endl;
	//	 	cout << puz<<endl;
			if(IsDiagBox5()) return 1;
		}
	}
	return 0;
}

int PUZC_SYM::IsDiagBox5(){
	if(!CheckBM(sd1_36[0],3))	return 0;
	// try all possibilities in band1 stack 1 to end 
	char zr[82];
	strcpy (zr ,puz);
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){
			MoveBxSx(zr,i,j,0,9);
			if(!CheckBM(sd1_36[3],12)) // partial final
				continue;
		//	cout<<"okpartial try now band3 stack 3"<<endl;
		//	cout << puz<<endl;
			// try now band3 stack 3
			char zr2[82];
			strcpy (zr2 ,puz);
			for(int i2=0;i2<6;i2++){
				for(int j2=0;j2<6;j2++){
					MoveBxSx(zr2,i2,j2,6,15);
				//	cout << "essai final " << i2 << " "<< j2 <<endl;
				//	cout << puz << endl;
					if(CheckBM(sd1_36[15],21)){
				//		cout << "found ok " <<endl;
						return 1;
					}
				}
			}
		}
	}
	return 0;
}

int PUZC_SYM::IsR90Boxes(){// not yet done already central
	if((!IsHBoxes()) ) return 0;
	if(nbox[0]-nbox[2])		return 0; // v boxes
	if(nbox[3]-nbox[5])		return 0;
	if(nbox[6]-nbox[8])		return 0;
	if(nbox[1]-nbox[5])		return 0;
	return 0;
};

int PUZC_SYM::IsStickBoxes(){ // 0 stick
	if(nbox[0]-nbox[6])		return 0;
	if(nbox[1]-nbox[7])		return 0;
	if(nbox[2]-nbox[8])		return 0;
	char zr[82];
	strcpy(zr,puz);
	for(int i=0;i<6;i++){
		for(int j=0;j<6;j++){ //try band3 stack1
			MoveBxSx(zr,i,j,6,9); // try to set a band3 ok
			if(!CheckBM(sst_36[0],12)) continue;
			char zr2[82];
			strcpy(zr2,puz);
			for(int s2=0;s2<6;s2++){ //try stack2
				MoveBSx(puz,zr2,s2,12); // try to set a band3 ok
				DoGiven();
				if(!CheckBM(sst_36[12],12)) continue;
				char zr3[82];
				strcpy(zr3,puz);
				for(int s3=0;s3<6;s3++){ //try stack2
					MoveBSx(puz,zr3,s3,15); // try to set a band3 ok
					DoGiven();
					if(CheckBM(sst_36[24],12)) return 1;
				}
			}
		}
	}


	return 0;
};

int PUZC_SYM::IsHBoxes(){ // H 0k
	if(nbox[0]-nbox[6])		return 0;
	if(nbox[1]-nbox[7])		return 0;
	if(nbox[2]-nbox[8])		return 0;
	char zr[82];
	strcpy(zr,puz);
	for(int i=0;i<6;i++){
		MoveBSx(puz,zr,i,3); // try to set a band2 ok
		DoGiven();
		if(!CheckBM(sh_36[27],9)) continue;
		char zr2[82];
		strcpy(zr2,puz);
		for(int j=0;j<6;j++){
			MoveBSx(puz,zr2,j,6); // try to set a band3 ok
			DoGiven();
			if(CheckBM(sh_36[0],27)) return 1;
		}
	}
	return 0;
};

//USHORT sym_81[5][81]

int PUZC_SYM::IsGivenxx(int isym){
	USHORT * ts=sym_81[isym] ;
	for(int i=0;i<9;i++) tpaires[i]=0;
	for(int i=0;i<81;i++)if(puz[i]-'.'){
		int c1=puz[i]-'1',i2=ts[i];
		if(puz[i2]=='.') // should never be
			return 0;
		int c2=puz[i2]-'1';
		if(tpaires[c1] && (!tpaires[c2]) )
			return 0;
		if(tpaires[c2] && (!tpaires[c1]) )
			return 0;
		int p1=tpaires[c1],p2=tpaires[c2];
		if(p1){
			if((p1-1-c2) || (p2-1-c1))
				return 0;
			continue;
		}
		tpaires[c1]=c2+1;
		tpaires[c2]=c1+1;
	}
	nsingles=0;
	singles.f=0;
	for(int i=0;i<9;i++) 
		if(tpaires[i]==(i+1) || (!tpaires[i])){
			tsingles[nsingles++]=i; // count self corresponding
			singles.Set(i);
		}

	if(isym==4) cout <<nsingles<<nsingles<<endl;

	if((!nsingles) || nsingles>3|| (isym>2 && nsingles>1))
		return 0;
	return 1;
}

int PUZC_SYM::IsGivenR90(){
	if(!CheckBM_R90()) return 0;
	for(int i=0;i<9;i++) tpaires[i]=0;
	for(int i=0;i<20;i++){
		USHORT *t4=sr90_20[i];
		if(puz[t4[0]]=='.') continue;
		int c1=puz[t4[0]]-'1',c2=puz[t4[1]]-'1',c3=puz[t4[2]]-'1',c4=puz[t4[3]]-'1';
		int p1=tpaires[c1],p2=tpaires[c2],p3=tpaires[c3],p4=tpaires[c4];
		if(p1 ){
			if((p1-1-c2) )				return 0;
		}
		else tpaires[c1]=c2+1;
		if(p2 ){
			if((p2-1-c3) )				return 0;
		}
		else tpaires[c2]=c3+1;
		if(p3 ){
			if((p3-1-c4) )				return 0;
		}
		else tpaires[c3]=c4+1;
		if(p4 ){
			if((p4-1-c1) )				return 0;
		}
		else tpaires[c4]=c1+1;
	}
	nsingles=0;
	singles.f=0;
	for(int i=0;i<9;i++) 
		if(tpaires[i]==(i+1) || (!tpaires[i])){
			tsingles[nsingles++]=i; // count self corresponding
			singles.Set(i);
		}
	if(nsingles-1)
		return 0;
	return 1;
}


GG::GG() {	// constructor
	pg = g[0]; 
	pg[81] = 0;
}

int GG::NBlancs() const {
	int i, n = 0;
	for(i = 0; i < 81; i++)
		if(pg[i] == '.')
			n++;   
	return n;
}

int GG::Nfix() const {
	int i, n = 0; 
	for(i = 0; i < 81; i++) 
		if(pg[i] - '.')
			n++;   
	return n;
}


void GG::Image(FSR * EE,char * lib) const {
	EE->E(lib); 
	EE->Enl(); 
//	char wc[10];
	for(int i=0;i<9;i++) {
//		strncpy_s(wc, 10, g[i], 9);
//		wc[9] = 0;
		EE->E(i + 1);
		EE->E("= ");
		for(int j1=0,j=0;j1<3;j1++){
			for(int j2=0;j2<3;j2++,j++){
				EE->E(g[i][j]);
			}
			EE->Esp();
		}
//		EE->E(wc);
		EE->Enl();
	}
}



int DIVF::IsObjetI(BF81 const &ze, int i) {
	return (ze.EstDans(cellsInHouseBM[i]));
}

int DIVF::IsObjet(const BF81 &ze) {
	for(int i = 0; i < 27; i++)
		if(IsObjetI(ze, i)) 
			return 1;  
	return 0;
}

int DIVF::IsBox(const BF81 &ze) {
	for(int i = 18; i < 27; i++) 
		if(IsObjetI(ze, i))
			return i; 
	return 0;
} 

int DIVF::IsObjet(USHORT p1, USHORT p2) {
	BF81 z(p1, p2);
	return IsObjet(z);
}

int DIVF::IsAutreObjet(const BF81 &ze, int obje, int &objs) {
	for(int i = 0; i < 27; i++) {
		if(i == obje)
			continue;
		if(IsObjetI(ze, i)) {
			objs=i;
			return 1; 
		}
	}
	return 0;
}
	//! Get valued cell count in the house <code>el</code>
int DIVF::N_Fixes(const char * pg, int el) {
	int n = 0; 
	for(int i = 0; i < 9; i++) 
		if(pg[cellsInGroup[el][i]] - '0')
			n++;
	return n;
}




 